#include "stdafx.h"
#include "../client/DCPlusPlus.h"
#include "Resource.h"

#include "EmoticonsDlg.h"
#include "WinUtil.h"
#include <math.h>

//#include <uxtheme.h>
//#include <vsstyle.h>

#include "MainFrm.h"

#ifndef AGEMOTIONSETUP_H__
#include "AGEmotionSetup.h"
#endif

//[?] #pragma comment(lib, "GdiOle.lib")

#define EMOTICONS_ICONMARGIN 8

extern CAGEmotionSetup* g_pEmotionsSetup;

WNDPROC EmoticonsDlg::m_MFCWndProc = 0;
EmoticonsDlg* EmoticonsDlg::m_pDialog = NULL;

LRESULT EmoticonsDlg::onEmoticonClick(WORD /*wNotifyCode*/, WORD /*wID*/, HWND hWndCtl, BOOL& /*bHandled*/)
{
	LocalArray<TCHAR, 256> buf;
	::GetWindowText(hWndCtl, buf, 255);
	result = buf;
	// pro ucely testovani emoticon packu...
	if ((GetKeyState(VK_SHIFT) & 0x8000) && (GetKeyState(VK_CONTROL) & 0x8000))
	{
		const CAGEmotion::List& Emoticons = g_pEmotionsSetup->getEmoticonsList();
		result = _T("");
		string lastEmotionPath, lastAnimEmotionPath;
		int l_count_emotion = 0;
		bool bUseAnimation = BOOLSETTING(SMILE_SELECT_WND_ANIM_SMILES);
		
		for (CAGEmotion::Iter pEmotion = Emoticons.begin();
		        pEmotion != Emoticons.end() && l_count_emotion < g_pEmotionsSetup->m_CountSelEmotions;
		        ++pEmotion, ++l_count_emotion)
		{
			if (bUseAnimation)
			{
				if (lastEmotionPath != (*pEmotion)->getEmotionBmpPath() || lastAnimEmotionPath != (*pEmotion)->getEmotionGifPath())
					result += (*pEmotion)->getEmotionText() + _T(" ");
					
				lastEmotionPath = (*pEmotion)->getEmotionBmpPath();
				lastAnimEmotionPath = (*pEmotion)->getEmotionGifPath();
			}
			else
			{
				if (lastEmotionPath != (*pEmotion)->getEmotionBmpPath())
					result += (*pEmotion)->getEmotionText() + _T(" ");
				lastEmotionPath = (*pEmotion)->getEmotionBmpPath();
			}
		}
	}
	PostMessage(WM_CLOSE);
	return 0;
}

LRESULT EmoticonsDlg::onInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	ShowWindow(SW_HIDE);
	WNDPROC temp = reinterpret_cast<WNDPROC>(::SetWindowLongPtr(EmoticonsDlg::m_hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(NewWndProc)));
	if (!m_MFCWndProc) m_MFCWndProc = temp;
	m_pDialog = this;
	::EnableWindow(WinUtil::mainWnd, true);
	
	bool bUseAnimation = BOOLSETTING(SMILE_SELECT_WND_ANIM_SMILES);
	
	if (g_pEmotionsSetup && g_pEmotionsSetup->getUseEmoticons() && SETTING(EMOTICONS_FILE) != "Disabled")
	{
	
		const CAGEmotion::List& Emoticons = g_pEmotionsSetup->getEmoticonsList();
		
		unsigned int pocet = 0;
		int l_count_emotion = 0;
		string lastEmotionPath, lastAnimEmotionPath;
		CAGEmotion::Iter pEmotion;
		for (pEmotion = Emoticons.begin();
		        pEmotion != Emoticons.end() && l_count_emotion < g_pEmotionsSetup->m_CountSelEmotions;
		        ++pEmotion, ++l_count_emotion)
		{
			if (bUseAnimation)
			{
				if ((*pEmotion)->getEmotionBmpPath() != lastEmotionPath || (*pEmotion)->getEmotionGifPath() != lastAnimEmotionPath)
					pocet++;
					
				lastEmotionPath = (*pEmotion)->getEmotionBmpPath();
				lastAnimEmotionPath = (*pEmotion)->getEmotionGifPath();
			}
			else
			{
				if ((*pEmotion)->getEmotionBmpPath() != lastEmotionPath)
					pocet++;
				lastEmotionPath = (*pEmotion)->getEmotionBmpPath();
			}
		}
		
		// x, y jen pro for cyklus
		const unsigned int l_Emoticons_size = g_pEmotionsSetup->m_CountSelEmotions;
		unsigned int i = (unsigned int)sqrt(double(l_Emoticons_size));
		unsigned int nXfor = i;
		unsigned int nYfor = i;
		
		if ((i * i) != l_Emoticons_size) //[+]PPA
		{
			nXfor = i + 1;
			if ((i * nXfor) < l_Emoticons_size) nYfor = i + 1;
			else nYfor = i;
		}
		// x, y pro korektni vkladani ikonek za sebou
		i = (unsigned int)sqrt((double)pocet);
		unsigned int nX = i;
		unsigned int nY = i;
		if ((i * i) != pocet) //[+]PPA
		{
			nX = i + 1;
			if ((i * nX) < pocet) nY = i + 1;
			else nY = i;
		}
		if (Emoticons.size() == 0 || !*Emoticons.begin()) //[+]PPA
			return 0;
			
		// [~] brain-ripper
		// If first icon failed to load, h_bm will be zero, and all icons will be drawn extremely small.
		// So cycle through Emoticons and find first loaded icon.
		//HBITMAP h_bm = (*Emoticons.begin())->getEmotionBmp(GetSysColor(COLOR_BTNFACE));
		
		DWORD iSW = 0, iSH = 0, dwCount = 0;
		l_count_emotion = 0;
		for (CAGEmotion::Iter i = Emoticons.begin(); i != Emoticons.end() && l_count_emotion < g_pEmotionsSetup->m_CountSelEmotions; ++i, l_count_emotion++)
		{
			int w = 0, h = 0;
			CGDIImage *pImage = NULL;
			
			if (bUseAnimation)
				pImage = (*i)->getAnimatedImage(MainFrame::getMainFrame()->m_hWnd, WM_ANIM_CHANGE_FRAME);
				
			if (pImage)
			{
				w = pImage->GetWidth();
				h = pImage->GetHeight();
				dwCount++;
			}
			else
			{
				if ((*i)->getDimensions(&w, &h))
				{
					if (bUseAnimation)
						dwCount++;
					else
					{
						iSW = w;
						iSH = h;
						break;
					}
				}
			}
			
			iSW += w * w;
			iSH += h * h;
		}
		
		if (bUseAnimation && dwCount)
		{
			// Get mean square of all icon dimensions
			iSW = DWORD(sqrt(float(iSW / dwCount)));
			iSH = DWORD(sqrt(float(iSH / dwCount)));
		}
		
		pos.bottom = pos.top - 3;
		pos.left = pos.right - nX * (iSW + EMOTICONS_ICONMARGIN) - 2;
		pos.top = pos.bottom - nY * (iSH + EMOTICONS_ICONMARGIN) - 2;
		
		// [+] brain-ripper
		// Fit window in screen's work area
		RECT rcWorkArea;
		SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
		if (pos.right > rcWorkArea.right)
		{
			pos.left -= pos.right - rcWorkArea.right;
			pos.right = rcWorkArea.right;
		}
		if (pos.bottom > rcWorkArea.bottom)
		{
			pos.top -= pos.bottom - rcWorkArea.bottom;
			pos.bottom = rcWorkArea.bottom;
		}
		if (pos.left < rcWorkArea.left)
		{
			pos.right += rcWorkArea.left - pos.left;
			pos.left = rcWorkArea.left;
		}
		if (pos.top < rcWorkArea.top)
		{
			pos.bottom += rcWorkArea.top - pos.top;
			pos.top = rcWorkArea.top;
		}
		
		MoveWindow(pos);
		
		pEmotion = Emoticons.begin();
		lastEmotionPath = Util::emptyString;
		lastAnimEmotionPath = Util::emptyString;
		
		ctrlToolTip.Create(EmoticonsDlg::m_hWnd, rcDefault, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON, WS_EX_TOPMOST);
		ctrlToolTip.SetDelayTime(TTDT_AUTOMATIC, 1000);
		
		pos.left = 0;
		pos.right = iSW + EMOTICONS_ICONMARGIN;
		pos.top = 0;
		pos.bottom = iSH + EMOTICONS_ICONMARGIN;
		
		HandleList.clear();
		for (unsigned int iY = 0; iY < nYfor; iY++)
			for (unsigned int iX = 0; iX < nXfor; iX++)
			{
				if ((iY * nXfor) + iX + 1 > l_Emoticons_size) break;
				
				bool bNotDuplicated = (bUseAnimation ? ((*pEmotion)->getEmotionBmpPath() != lastEmotionPath || (*pEmotion)->getEmotionGifPath() != lastAnimEmotionPath) : (*pEmotion)->getEmotionBmpPath() != lastEmotionPath);
				
				bool bCreated = false;
				
				// dve stejne emotikony za sebou nechceme
				if (bNotDuplicated)
				{
					CGDIImage *pImage = NULL;
					
					if (bUseAnimation)
						pImage = (*pEmotion)->getAnimatedImage(MainFrame::getMainFrame()->m_hWnd, WM_ANIM_CHANGE_FRAME);
						
					if (pImage)
					{
						wstring str = (*pEmotion)->getEmotionText().c_str();
						CAnimatedButton *pemoButton = new CAnimatedButton(pImage);
						m_BtnList.push_back(pemoButton);
						pemoButton->Create(EmoticonsDlg::m_hWnd, pos, (*pEmotion)->getEmotionText().c_str(), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | BS_FLAT | BS_BITMAP, WS_EX_TRANSPARENT);
						
						tstring smajl = (*pEmotion)->getEmotionText();
						CToolInfo ti(TTF_SUBCLASS, pemoButton->m_hWnd, 0, 0, const_cast<LPTSTR>(smajl.c_str()));
						ctrlToolTip.AddTool(&ti);
						bCreated = true;
					}
					else
					{
						HBITMAP l_h_bm = (*pEmotion)->getEmotionBmp(GetSysColor(COLOR_BTNFACE));
						
						if (l_h_bm)
						{
							CButton emoButton;
							emoButton.Create(EmoticonsDlg::m_hWnd, pos, (*pEmotion)->getEmotionText().c_str(), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | BS_FLAT | BS_BITMAP | BS_CENTER);
							HandleList.push_back(l_h_bm);
							emoButton.SetBitmap(l_h_bm);
							
							tstring smajl = (*pEmotion)->getEmotionText();
							CToolInfo ti(TTF_SUBCLASS, emoButton.m_hWnd, 0, 0, const_cast<LPTSTR>(smajl.c_str()));
							ctrlToolTip.AddTool(&ti);
							
							DeleteObject((HGDIOBJ)emoButton);
							bCreated = true;
						}
					}
					
					if (bCreated)
					{
						// Calculate position of next button
						pos.left = pos.left + iSW + EMOTICONS_ICONMARGIN;
						pos.right = pos.left + iSW + EMOTICONS_ICONMARGIN;
						if (pos.left >= (LONG)(nX * (iSW + EMOTICONS_ICONMARGIN)))
						{
							pos.left = 0;
							pos.right = iSW + EMOTICONS_ICONMARGIN;
							pos.top = pos.top + iSH + EMOTICONS_ICONMARGIN;
							pos.bottom = pos.top + iSH + EMOTICONS_ICONMARGIN;
						}
					}
				}
				
				lastEmotionPath = (*pEmotion)->getEmotionBmpPath();
				
				if (bUseAnimation)
					lastAnimEmotionPath = (*pEmotion)->getEmotionGifPath();
				++pEmotion;
			}
			
		pos.left = -128;
		pos.right = pos.left;
		pos.top = -128;
		pos.bottom = pos.top;
		CButton emoButton;
		emoButton.Create(EmoticonsDlg::m_hWnd, pos, _T(""), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | BS_FLAT);
		emoButton.SetFocus();
		DeleteObject((HGDIOBJ)emoButton);
		ShowWindow(SW_SHOW);
		
		for (tBTN_LIST::iterator i = m_BtnList.begin(); i != m_BtnList.end(); ++i)
		{
			(*i)->Update();
		}
	}
	else PostMessage(WM_CLOSE);
	
	return 0;
}

LRESULT CALLBACK EmoticonsDlg::NewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message == WM_ACTIVATE && wParam == 0)
	{
		m_pDialog->PostMessage(WM_CLOSE);
		return FALSE;
	}
	return ::CallWindowProc(m_MFCWndProc, hWnd, message, wParam, lParam);
}


EmoticonsDlg::~EmoticonsDlg()
{
	for (tBTN_LIST::iterator i = m_BtnList.begin(); i != m_BtnList.end(); ++i)
	{
		delete *i;
	}
}

CAnimatedButton::CAnimatedButton(CGDIImage *pImage):
	m_pImage(pImage), m_hDC(NULL), m_bInited(false)
{
	if (m_pImage)
	{
		m_pImage->AddRef();
	}
}

CAnimatedButton::~CAnimatedButton()
{
	BOOL bNull;
	onClose(0, 0, 0, bNull);
}

bool CAnimatedButton::OnFrameChanged(CGDIImage *pImage, LPARAM lParam)
{
	CAnimatedButton *pBtn = (CAnimatedButton*)lParam;
	
	if (pBtn->m_hDC)
	{
		pImage->Draw(pBtn->m_hDC,
		             0,
		             0,
		             pBtn->m_w,
		             pBtn->m_h,
		             pBtn->m_xSrc,
		             pBtn->m_ySrc,
		             pBtn->m_hBackDC,
		             pBtn->m_xBk,
		             pBtn->m_yBk,
		             pBtn->m_wSrc,
		             pBtn->m_hSrc);
	}
	
	return true;
}

LRESULT CAnimatedButton::onPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
	ValidateRect(NULL);
	
	bHandled = false;
	
	return 1;
}

LRESULT CAnimatedButton::onCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	if (m_pImage)
	{
		RECT rect;
		GetClientRect(&rect);
		
		m_w = rect.right - rect.left;
		m_h = rect.bottom - rect.top;
		int img_w = m_pImage->GetWidth();
		int img_h = m_pImage->GetHeight();
		
		m_hDC = ::GetDC(m_hWnd);
		m_hBackDC = m_pImage->CreateBackDC(GetSysColor(COLOR_BTNFACE), m_w, m_h);
		
		int iOffsetX = (m_w - img_w) / 2;
		int iOffsetY = (m_h - img_h) / 2;
		
		m_xBk = iOffsetX >= EMOTICONS_ICONMARGIN / 2 ? iOffsetX : EMOTICONS_ICONMARGIN / 2;
		m_yBk = iOffsetY >= EMOTICONS_ICONMARGIN / 2 ? iOffsetY : EMOTICONS_ICONMARGIN / 2;
		
		m_xSrc = iOffsetX >= EMOTICONS_ICONMARGIN / 2 ? 0 : EMOTICONS_ICONMARGIN / 2 - iOffsetX;
		m_ySrc = iOffsetY >= EMOTICONS_ICONMARGIN / 2 ? 0 : EMOTICONS_ICONMARGIN / 2 - iOffsetY;
		
		m_wSrc = img_w - m_xSrc * 2;
		m_hSrc = img_h - m_ySrc * 2;
		
		/*
		HTHEME hTheme = OpenThemeData(m_hWnd, L"Button");
		if (hTheme)
		{
		    DrawThemeBackground(hTheme, m_hBackDC, BP_PUSHBUTTON, PBS_NORMAL, &rect, NULL);
		    CloseThemeData(hTheme);
		}
		else
		    DrawFrameControl(m_hBackDC, &rect, DFC_BUTTON, DFCS_FLAT | DFCS_BUTTONPUSH);
		*/
		
		DrawFrameControl(m_hBackDC, &rect, DFC_BUTTON, DFCS_FLAT | DFCS_BUTTONPUSH);
		
		//BitBlt(m_hBackDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, 0, 0, BLACKNESS);
		m_pImage->RegisterCallback(OnFrameChanged, (LPARAM)this);
		m_bInited = true;
	}
	
	return 0;
}

LRESULT CAnimatedButton::onErase(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
	ValidateRect(NULL);
	bHandled = true;
	return 0;
}

LRESULT CAnimatedButton::onClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	if (m_pImage)
	{
		if (m_bInited)
		{
			m_pImage->UnregisterCallback(OnFrameChanged, (LPARAM)this);
			m_pImage->DeleteBackDC(m_hBackDC);
		}
		
		m_pImage->Release();
		m_pImage = NULL;
	}
	
	if (m_hDC)
	{
		::ReleaseDC(m_hWnd, m_hDC);
		m_hDC = NULL;
	}
	
	return 0;
}

void CAnimatedButton::Update()
{
	if (m_hDC)
	{
		m_pImage->Draw(m_hDC,
		               0,
		               0,
		               m_w,
		               m_h,
		               m_xSrc,
		               m_ySrc,
		               m_hBackDC,
		               m_xBk,
		               m_yBk,
		               m_wSrc,
		               m_hSrc);
	}
}